#include "include/hd.h"
#include "include/irq.h"
#include "include/proc.h"
#include "include/kconst.h"
#include "include/printf.h"
#include "include/klib.h"
#include "include/misc.h"
#include "include/syscall.h"
/* 初始化硬盘中断 */
u8	hdbuf[SECTOR_SIZE * 2];
u8	hd_status;
int waitfor(int mask, int val, int timeout)
{
    int t = get_ticks();
    while ((get_ticks() - t) * 10 < timeout)
    {
        if ((in_byte(REG_STATUS) & mask) == val)
            return 1;
    }
    return 0;
}

void interrupt_wait()
{
    MESSAGE msg;
    send_recv(RECEIVE, INTERRUPT, &msg);
}

void hd_handler(int irq)
{
    hd_status = in_byte(REG_STATUS);
    inform_int(TASK_HD);
}


void init_hd()
{
    u8 *p_num_drives = (u8*) (0x475);
    printf("number of drives: %d", *p_num_drives);
    assert(*p_num_drives);

    put_irq_handler(AT_WINI_IRQ, hd_handler);
    enable_irq(CASCADE_IRQ);
    enable_irq(AT_WINI_IRQ);
}

void hd_cmd_out(struct hd_cmd *cmd)
{
    if (!waitfor(STATUS_BSY, 0, HD_TIMEOUT))
        panic("hd error.");
    out_byte(REG_DEV_CTRL, 0);
    out_byte(REG_FEATURES, cmd->features);
    out_byte(REG_NSECTOR, cmd->count);
    out_byte(REG_LBA_LOW, cmd->lba_low);
    out_byte(REG_LBA_MID, cmd->lba_mid);
    out_byte(REG_LBA_HIGH, cmd->lba_high);
    out_byte(REG_DEVICE, cmd->device);
    out_byte(REG_CMD, cmd->command);
}

void printf_identify_info(u16 * hdinfo)
{
    int i, k;
	char s[64];
    int capabilities = hdinfo[49];
    int cmd_set_supported = hdinfo[83];
    int sectors = ((int)hdinfo[61] << 16) + hdinfo[60];
    struct iden_info_ascii {
        u32 idx;
        u32 len;
        char *desc;
    } iinfo[] = {
        {10, 20, "HD SN"},
        {27, 40, "HD Model"}
    };
    printf("\n\n");
    for (k = 0;k < sizeof(iinfo)/sizeof(iinfo[0]);k++)
    {
        char *p = (char*) &hdinfo[iinfo[k].idx];
        for (i = 0; i < iinfo[k].len/2;i++)
        {
            s[i*2+1] = *p++;
            s[i*2] = *p++;
        }
        s[i*2] = 0;
        printf("%s: %s\n", iinfo[k].desc, s);
    }
    printf("HD size: %dMB\n", sectors*512 / 1048576);
    printf("LBA supported: %s\n", (capabilities & 0x200) ? "Yes" : "No");
    printf("LBA48 supported: %s\n", (cmd_set_supported & 0x400) ? "Yes" : "No");
    
}

void hd_identify(int drive)
{
    struct hd_cmd cmd;
    cmd.device = MAKE_DEVICE_REG(0, drive, 0);
    cmd.command = ATA_IDENTIFY;
    hd_cmd_out(&cmd);
    interrupt_wait();
    port_read(REG_DATA, hdbuf, SECTOR_SIZE);
    printf_identify_info((u16*)hdbuf);
}

/* 硬盘驱动程序 */
void task_hd()
{
    MESSAGE msg;
    init_hd();
    printf("task_hd started.");
    while (1) {
        send_recv(RECEIVE, ANY, &msg);
        int src = msg.source;
        switch (msg.type)
        {
        case DEV_OPEN:
            hd_identify(0);
            break;
        
        default:
            break;
        }
        send_recv(SEND, src, &msg);
    }
}